{
  "cells": [
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T02:32:19.455566Z",
          "start_time": "2025-01-17T02:32:19.450543Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "J3UhUJz4WpTk",
        "outputId": "a2a769ad-354e-4356-b44e-e168a26fefb5"
      },
      "source": [
        "import matplotlib as mpl\n",
        "import matplotlib.pyplot as plt\n",
        "%matplotlib inline\n",
        "import numpy as np\n",
        "import sklearn\n",
        "import pandas as pd\n",
        "import os\n",
        "import sys\n",
        "import time\n",
        "from tqdm.auto import tqdm\n",
        "import torch\n",
        "import torch.nn as nn\n",
        "import torch.nn.functional as F\n",
        "\n",
        "print(sys.version_info)\n",
        "for module in mpl, np, pd, sklearn, torch:\n",
        "    print(module.__name__, module.__version__)\n",
        "\n",
        "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
        "print(device)\n"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "sys.version_info(major=3, minor=11, micro=11, releaselevel='final', serial=0)\n",
            "matplotlib 3.10.0\n",
            "numpy 1.26.4\n",
            "pandas 2.2.2\n",
            "sklearn 1.6.0\n",
            "torch 2.5.1+cu121\n",
            "cuda:0\n"
          ]
        }
      ],
      "execution_count": 1
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7PJig9UMWpTo"
      },
      "source": [
        "## 加载数据"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T02:32:19.540144Z",
          "start_time": "2025-01-17T02:32:19.507290Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "eBKUU4TJWpTq",
        "outputId": "ce26cf1d-b59d-4b03-d4d2-0689c443b64f"
      },
      "source": [
        "from torchvision import datasets\n",
        "from torchvision.transforms import ToTensor\n",
        "\n",
        "# fashion_mnist图像分类数据集\n",
        "train_ds = datasets.FashionMNIST(\n",
        "    root=\"data\",\n",
        "    train=True,\n",
        "    download=True,\n",
        "    transform=ToTensor()\n",
        ")\n",
        "\n",
        "test_ds = datasets.FashionMNIST(\n",
        "    root=\"data\",\n",
        "    train=False,\n",
        "    download=True,\n",
        "    transform=ToTensor()\n",
        ")\n",
        "\n",
        "# torchvision 数据集里没有提供训练集和验证集的划分\n",
        "# 当然也可以用 torch.utils.data.Dataset 实现人为划分\n",
        "# 从数据集到dataloader\n",
        "train_loader = torch.utils.data.DataLoader(train_ds, batch_size=16, shuffle=True)\n",
        "val_loader = torch.utils.data.DataLoader(test_ds, batch_size=16, shuffle=False)"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz\n",
            "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz to data/FashionMNIST/raw/train-images-idx3-ubyte.gz\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "100%|██████████| 26.4M/26.4M [00:02<00:00, 13.0MB/s]\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Extracting data/FashionMNIST/raw/train-images-idx3-ubyte.gz to data/FashionMNIST/raw\n",
            "\n",
            "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gz\n",
            "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gz to data/FashionMNIST/raw/train-labels-idx1-ubyte.gz\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "100%|██████████| 29.5k/29.5k [00:00<00:00, 212kB/s]\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Extracting data/FashionMNIST/raw/train-labels-idx1-ubyte.gz to data/FashionMNIST/raw\n",
            "\n",
            "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gz\n",
            "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gz to data/FashionMNIST/raw/t10k-images-idx3-ubyte.gz\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "100%|██████████| 4.42M/4.42M [00:01<00:00, 3.86MB/s]\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Extracting data/FashionMNIST/raw/t10k-images-idx3-ubyte.gz to data/FashionMNIST/raw\n",
            "\n",
            "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gz\n",
            "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gz to data/FashionMNIST/raw/t10k-labels-idx1-ubyte.gz\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "100%|██████████| 5.15k/5.15k [00:00<00:00, 6.09MB/s]"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Extracting data/FashionMNIST/raw/t10k-labels-idx1-ubyte.gz to data/FashionMNIST/raw\n",
            "\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "\n"
          ]
        }
      ],
      "execution_count": 2
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T02:32:22.296468Z",
          "start_time": "2025-01-17T02:32:19.541152Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "xg10heReWpTr",
        "outputId": "42f966f2-208a-4fd0-8bc8-894894d7b732"
      },
      "source": [
        "from torchvision.transforms import Normalize\n",
        "\n",
        "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
        "def cal_mean_std(ds):\n",
        "    mean = 0.\n",
        "    std = 0.\n",
        "    for img, _ in ds:\n",
        "        mean += img.mean(dim=(1, 2))\n",
        "        std += img.std(dim=(1, 2))\n",
        "    mean /= len(ds)\n",
        "    std /= len(ds)\n",
        "    return mean, std\n",
        "\n",
        "\n",
        "print(cal_mean_std(train_ds))\n",
        "# 0.2860， 0.3205\n",
        "transforms = nn.Sequential(\n",
        "    Normalize([0.2860], [0.3205])\n",
        ")\n"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "(tensor([0.2860]), tensor([0.3205]))\n"
          ]
        }
      ],
      "execution_count": 3
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "gk63GymQWpTs"
      },
      "source": [
        "## 定义模型\n",
        "\n",
        "这里我们没有用`nn.Linear`的默认初始化，而是采用了xavier均匀分布去初始化全连接层的权重\n",
        "\n",
        "xavier初始化出自论文 《Understanding the difficulty of training deep feedforward neural networks》，适用于使用`tanh`和`sigmoid`激活函数的方法。当然，我们这里的模型采用的是`relu`激活函数，采用He初始化（何凯明初始化）会更加合适。感兴趣的同学可以自己动手修改并比对效果。\n",
        "\n",
        "|神经网络层数|初始化方式|early stop at epoch| val_loss | vla_acc|\n",
        "|-|-|-|-|-|\n",
        "|20|默认|\n",
        "|20|xaviier_uniform|\n",
        "|20|he_uniform|\n",
        "|...|\n",
        "\n",
        "He初始化出自论文 《Delving deep into rectifiers: Surpassing human-level performance on ImageNet classification》"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T02:32:22.304974Z",
          "start_time": "2025-01-17T02:32:22.296468Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "-RgBpvoBWpTs",
        "outputId": "1bd6ef85-bbf6-40c8-cb50-9504e36251ac"
      },
      "source": [
        "class NeuralNetwork(nn.Module):\n",
        "    def __init__(self, layers_num=2):\n",
        "        super().__init__()\n",
        "        self.transforms = transforms\n",
        "        self.flatten = nn.Flatten()\n",
        "        # 多加几层\n",
        "        self.linear_relu_stack = nn.Sequential(\n",
        "            nn.Linear(28 * 28, 100),  # in_features=784, out_features=300\n",
        "            nn.SELU(),\n",
        "        )\n",
        "        # 加19层\n",
        "        for i in range(1, layers_num):\n",
        "            self.linear_relu_stack.add_module(f\"Linear_{i}\", nn.Linear(100, 100))\n",
        "            self.linear_relu_stack.add_module(f\"selu\", nn.SELU()) # 这里采用SELU激活函数\n",
        "        # 输出层\n",
        "        self.linear_relu_stack.add_module(\"Output Layer\", nn.Linear(100, 10))\n",
        "\n",
        "        # 初始化权重\n",
        "        self.init_weights()\n",
        "\n",
        "    def init_weights(self):\n",
        "        \"\"\"使用 xavier 均匀分布来初始化全连接层的权重 W\"\"\"\n",
        "        for m in self.modules():\n",
        "            if isinstance(m, nn.Linear):\n",
        "                nn.init.xavier_uniform_(m.weight)\n",
        "                nn.init.zeros_(m.bias)\n",
        "\n",
        "    def forward(self, x):\n",
        "        # x.shape [batch size, 1, 28, 28]\n",
        "        x = self.transforms(x)\n",
        "        x = self.flatten(x)\n",
        "        # 展平后 x.shape [batch size, 28 * 28]\n",
        "        logits = self.linear_relu_stack(x)\n",
        "        # logits.shape [batch size, 10]\n",
        "        return logits\n",
        "\n",
        "for idx, (key, value) in enumerate(NeuralNetwork(20).named_parameters()):\n",
        "    print(f\"Linear_{idx // 2:>02}\\tparamerters num: {np.prod(value.shape)}\")"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Linear_00\tparamerters num: 78400\n",
            "Linear_00\tparamerters num: 100\n",
            "Linear_01\tparamerters num: 10000\n",
            "Linear_01\tparamerters num: 100\n",
            "Linear_02\tparamerters num: 10000\n",
            "Linear_02\tparamerters num: 100\n",
            "Linear_03\tparamerters num: 10000\n",
            "Linear_03\tparamerters num: 100\n",
            "Linear_04\tparamerters num: 10000\n",
            "Linear_04\tparamerters num: 100\n",
            "Linear_05\tparamerters num: 10000\n",
            "Linear_05\tparamerters num: 100\n",
            "Linear_06\tparamerters num: 10000\n",
            "Linear_06\tparamerters num: 100\n",
            "Linear_07\tparamerters num: 10000\n",
            "Linear_07\tparamerters num: 100\n",
            "Linear_08\tparamerters num: 10000\n",
            "Linear_08\tparamerters num: 100\n",
            "Linear_09\tparamerters num: 10000\n",
            "Linear_09\tparamerters num: 100\n",
            "Linear_10\tparamerters num: 10000\n",
            "Linear_10\tparamerters num: 100\n",
            "Linear_11\tparamerters num: 10000\n",
            "Linear_11\tparamerters num: 100\n",
            "Linear_12\tparamerters num: 10000\n",
            "Linear_12\tparamerters num: 100\n",
            "Linear_13\tparamerters num: 10000\n",
            "Linear_13\tparamerters num: 100\n",
            "Linear_14\tparamerters num: 10000\n",
            "Linear_14\tparamerters num: 100\n",
            "Linear_15\tparamerters num: 10000\n",
            "Linear_15\tparamerters num: 100\n",
            "Linear_16\tparamerters num: 10000\n",
            "Linear_16\tparamerters num: 100\n",
            "Linear_17\tparamerters num: 10000\n",
            "Linear_17\tparamerters num: 100\n",
            "Linear_18\tparamerters num: 10000\n",
            "Linear_18\tparamerters num: 100\n",
            "Linear_19\tparamerters num: 10000\n",
            "Linear_19\tparamerters num: 100\n",
            "Linear_20\tparamerters num: 1000\n",
            "Linear_20\tparamerters num: 10\n"
          ]
        }
      ],
      "execution_count": 4
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5nkdvxwqWpTt"
      },
      "source": [
        "## 训练"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T02:32:22.308348Z",
          "start_time": "2025-01-17T02:32:22.304974Z"
        },
        "id": "p0uEQz_WWpTt"
      },
      "source": [
        "from sklearn.metrics import accuracy_score\n",
        "\n",
        "@torch.no_grad()\n",
        "def evaluating(model, dataloader, loss_fct):\n",
        "    loss_list = []\n",
        "    pred_list = []\n",
        "    label_list = []\n",
        "    for datas, labels in dataloader:\n",
        "        datas = datas.to(device)\n",
        "        labels = labels.to(device)\n",
        "        # 前向计算\n",
        "        logits = model(datas)\n",
        "        loss = loss_fct(logits, labels)         # 验证集损失\n",
        "        loss_list.append(loss.item())\n",
        "\n",
        "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
        "        pred_list.extend(preds.cpu().numpy().tolist())\n",
        "        label_list.extend(labels.cpu().numpy().tolist())\n",
        "\n",
        "    acc = accuracy_score(label_list, pred_list)\n",
        "    return np.mean(loss_list), acc\n"
      ],
      "outputs": [],
      "execution_count": 5
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T02:32:22.313015Z",
          "start_time": "2025-01-17T02:32:22.308348Z"
        },
        "id": "i17fA1v6WpTu"
      },
      "source": [
        "from torch.utils.tensorboard import SummaryWriter\n",
        "\n",
        "\n",
        "class TensorBoardCallback:\n",
        "    def __init__(self, log_dir, flush_secs=10):\n",
        "        \"\"\"\n",
        "        Args:\n",
        "            log_dir (str): dir to write log.\n",
        "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
        "        \"\"\"\n",
        "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
        "\n",
        "    def draw_model(self, model, input_shape):\n",
        "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
        "\n",
        "    def add_loss_scalars(self, step, loss, val_loss):\n",
        "        self.writer.add_scalars(\n",
        "            main_tag=\"training/loss\",\n",
        "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
        "            global_step=step,\n",
        "            )\n",
        "\n",
        "    def add_acc_scalars(self, step, acc, val_acc):\n",
        "        self.writer.add_scalars(\n",
        "            main_tag=\"training/accuracy\",\n",
        "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
        "            global_step=step,\n",
        "        )\n",
        "\n",
        "    def add_lr_scalars(self, step, learning_rate):\n",
        "        self.writer.add_scalars(\n",
        "            main_tag=\"training/learning_rate\",\n",
        "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
        "            global_step=step,\n",
        "\n",
        "        )\n",
        "\n",
        "    def __call__(self, step, **kwargs):\n",
        "        # add loss\n",
        "        loss = kwargs.pop(\"loss\", None)\n",
        "        val_loss = kwargs.pop(\"val_loss\", None)\n",
        "        if loss is not None and val_loss is not None:\n",
        "            self.add_loss_scalars(step, loss, val_loss)\n",
        "        # add acc\n",
        "        acc = kwargs.pop(\"acc\", None)\n",
        "        val_acc = kwargs.pop(\"val_acc\", None)\n",
        "        if acc is not None and val_acc is not None:\n",
        "            self.add_acc_scalars(step, acc, val_acc)\n",
        "        # add lr\n",
        "        learning_rate = kwargs.pop(\"lr\", None)\n",
        "        if learning_rate is not None:\n",
        "            self.add_lr_scalars(step, learning_rate)\n"
      ],
      "outputs": [],
      "execution_count": 6
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T02:32:22.316641Z",
          "start_time": "2025-01-17T02:32:22.313015Z"
        },
        "id": "Y1zW-KP7WpTv"
      },
      "source": [
        "class SaveCheckpointsCallback:\n",
        "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
        "        \"\"\"\n",
        "        Save checkpoints each save_epoch epoch.\n",
        "        We save checkpoint by epoch in this implementation.\n",
        "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
        "\n",
        "        Args:\n",
        "            save_dir (str): dir to save checkpoint\n",
        "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
        "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
        "        \"\"\"\n",
        "        self.save_dir = save_dir\n",
        "        self.save_step = save_step\n",
        "        self.save_best_only = save_best_only\n",
        "        self.best_metrics = -1\n",
        "\n",
        "        # mkdir\n",
        "        if not os.path.exists(self.save_dir):\n",
        "            os.mkdir(self.save_dir)\n",
        "\n",
        "    def __call__(self, step, state_dict, metric=None):\n",
        "        if step % self.save_step > 0:\n",
        "            return\n",
        "\n",
        "        if self.save_best_only:\n",
        "            assert metric is not None\n",
        "            if metric >= self.best_metrics:\n",
        "                # save checkpoints\n",
        "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
        "                # update best metrics\n",
        "                self.best_metrics = metric\n",
        "        else:\n",
        "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
        "\n"
      ],
      "outputs": [],
      "execution_count": 7
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T02:32:22.319719Z",
          "start_time": "2025-01-17T02:32:22.316641Z"
        },
        "id": "i2kqlLeWWpTv"
      },
      "source": [
        "class EarlyStopCallback:\n",
        "    def __init__(self, patience=5, min_delta=0.01):\n",
        "        \"\"\"\n",
        "\n",
        "        Args:\n",
        "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
        "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute\n",
        "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
        "        \"\"\"\n",
        "        self.patience = patience\n",
        "        self.min_delta = min_delta\n",
        "        self.best_metric = -1\n",
        "        self.counter = 0\n",
        "\n",
        "    def __call__(self, metric):\n",
        "        if metric >= self.best_metric + self.min_delta:\n",
        "            # update best metric\n",
        "            self.best_metric = metric\n",
        "            # reset counter\n",
        "            self.counter = 0\n",
        "        else:\n",
        "            self.counter += 1\n",
        "\n",
        "    @property\n",
        "    def early_stop(self):\n",
        "        return self.counter >= self.patience\n"
      ],
      "outputs": [],
      "execution_count": 8
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T02:42:01.375878Z",
          "start_time": "2025-01-17T02:32:22.319719Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 67,
          "referenced_widgets": [
            "12017d6b533f4fe2a4085f50905ae448",
            "9ea0c658e6204fb395ca23e8e6bd6039",
            "e9e545daa3774bdb8431d88076cd5fe8",
            "a06b865199ee441ca032bbeed9cea15d",
            "c7096770fb5c48be88bc787bf5adaee8",
            "2105ca87b9d54be8a5a307bdba6c4bcf",
            "fef0af365c604b1eb7b02ac9cf5dae82",
            "b84f4f37873e475094cc86f7225999a9",
            "03c18b8d85d245bc893378847eb978c5",
            "f54afdcc184f40e7a1cfe29da99ebe60",
            "289d71f847e148dda2fa9149bfdd652b"
          ]
        },
        "id": "BQZJclb-WpTw",
        "outputId": "7ad77d77-8113-46fe-cc50-ed2bb03d6ddc"
      },
      "source": [
        "# 训练\n",
        "def training(\n",
        "    model,\n",
        "    train_loader,\n",
        "    val_loader,\n",
        "    epoch,\n",
        "    loss_fct,\n",
        "    optimizer,\n",
        "    tensorboard_callback=None,\n",
        "    save_ckpt_callback=None,\n",
        "    early_stop_callback=None,\n",
        "    eval_step=500,\n",
        "    ):\n",
        "    record_dict = {\n",
        "        \"train\": [],\n",
        "        \"val\": []\n",
        "    }\n",
        "\n",
        "    global_step = 0\n",
        "    model.train()\n",
        "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
        "        for epoch_id in range(epoch):\n",
        "            # training\n",
        "            for datas, labels in train_loader:\n",
        "                datas = datas.to(device)\n",
        "                labels = labels.to(device)\n",
        "                # 梯度清空\n",
        "                optimizer.zero_grad()\n",
        "                # 模型前向计算\n",
        "                logits = model(datas)\n",
        "                # 计算损失\n",
        "                loss = loss_fct(logits, labels)\n",
        "                # 梯度回传\n",
        "                loss.backward()\n",
        "                # 调整优化器，包括学习率的变动等\n",
        "                optimizer.step()\n",
        "                preds = logits.argmax(axis=-1)\n",
        "\n",
        "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())\n",
        "                loss = loss.cpu().item()\n",
        "                # record\n",
        "\n",
        "                record_dict[\"train\"].append({\n",
        "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
        "                })\n",
        "\n",
        "                # evaluating\n",
        "                if global_step % eval_step == 0:\n",
        "                    model.eval()\n",
        "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
        "                    record_dict[\"val\"].append({\n",
        "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
        "                    })\n",
        "                    model.train()\n",
        "\n",
        "                    # 1. 使用 tensorboard 可视化\n",
        "                    if tensorboard_callback is not None:\n",
        "                        tensorboard_callback(\n",
        "                            global_step,\n",
        "                            loss=loss, val_loss=val_loss,\n",
        "                            acc=acc, val_acc=val_acc,\n",
        "                            lr=optimizer.param_groups[0][\"lr\"],\n",
        "                            )\n",
        "\n",
        "                    # 2. 保存模型权重 save model checkpoint\n",
        "                    if save_ckpt_callback is not None:\n",
        "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
        "\n",
        "                    # 3. 早停 Early Stop\n",
        "                    if early_stop_callback is not None:\n",
        "                        early_stop_callback(val_acc)\n",
        "                        if early_stop_callback.early_stop:\n",
        "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
        "                            return record_dict\n",
        "\n",
        "                # udate step\n",
        "                global_step += 1\n",
        "                pbar.update(1)\n",
        "                pbar.set_postfix({\"epoch\": epoch_id})\n",
        "\n",
        "    return record_dict\n",
        "\n",
        "\n",
        "epoch = 100\n",
        "\n",
        "model = NeuralNetwork(layers_num=10)\n",
        "\n",
        "# 1. 定义损失函数 采用交叉熵损失\n",
        "loss_fct = nn.CrossEntropyLoss()\n",
        "# 2. 定义优化器 采用SGD\n",
        "# Optimizers specified in the torch.optim package\n",
        "optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)\n",
        "\n",
        "# 1. tensorboard 可视化\n",
        "tensorboard_callback = TensorBoardCallback(\"runs/selu\")\n",
        "tensorboard_callback.draw_model(model, [1, 28, 28])\n",
        "# 2. save best\n",
        "if not os.path.exists(\"checkpoints/selu\"):\n",
        "    #创建多级目录\n",
        "    os.makedirs(\"checkpoints/selu\")\n",
        "save_ckpt_callback = SaveCheckpointsCallback(\"checkpoints/selu\", save_best_only=True)\n",
        "# 3. early stop\n",
        "early_stop_callback = EarlyStopCallback(patience=10, min_delta=0.001)\n",
        "\n",
        "model = model.to(device)\n",
        "record = training(\n",
        "    model,\n",
        "    train_loader,\n",
        "    val_loader,\n",
        "    epoch,\n",
        "    loss_fct,\n",
        "    optimizer,\n",
        "    tensorboard_callback=tensorboard_callback,\n",
        "    save_ckpt_callback=save_ckpt_callback,\n",
        "    early_stop_callback=early_stop_callback,\n",
        "    eval_step=len(train_loader)\n",
        "    )"
      ],
      "outputs": [
        {
          "data": {
            "application/vnd.jupyter.widget-view+json": {
              "model_id": "12017d6b533f4fe2a4085f50905ae448",
              "version_major": 2,
              "version_minor": 0
            },
            "text/plain": [
              "  0%|          | 0/375000 [00:00<?, ?it/s]"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Early stop at epoch 27 / global_step 101250\n"
          ]
        }
      ],
      "execution_count": 9
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T02:42:01.525386Z",
          "start_time": "2025-01-17T02:42:01.375878Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 465
        },
        "id": "LUbGE1iZWpTw",
        "outputId": "b0b9a808-6992-4add-9cfe-787a5b872390"
      },
      "source": [
        "#画线要注意的是损失是不一定在零到1之间的\n",
        "def plot_learning_curves(record_dict, sample_step=500):\n",
        "    # build DataFrame\n",
        "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
        "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
        "\n",
        "    # plot\n",
        "    fig_num = len(train_df.columns)\n",
        "    fig, axs = plt.subplots(1, fig_num, figsize=(6 * fig_num, 5))\n",
        "    for idx, item in enumerate(train_df.columns):\n",
        "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
        "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
        "        axs[idx].grid()\n",
        "        axs[idx].legend()\n",
        "        axs[idx].set_xlabel(\"step\")\n",
        "\n",
        "    plt.show()\n",
        "\n",
        "plot_learning_curves(record, sample_step=10000)  #横坐标是 steps"
      ],
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 1200x500 with 2 Axes>"
            ],
            "image/png": "iVBORw0KGgoAAAANSUhEUgAAA9gAAAHACAYAAABZMMCuAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAoDFJREFUeJzs3Xd8m9XZ//GPJMvyXnE8Ejt7k0kgIQmbDAhQQgulwFNGH2gZ6QMESklbRqCQFsoqKy2UpvQHBUoLtBAgJiEESEggIXtPO8OOE8fblmRJvz9uS7YTD8mWvPR9v156ad33raPjKNJ1X+ecy+TxeDyIiIiIiIiISJuYO7oBIiIiIiIiIt2BAmwRERERERGRIFCALSIiIiIiIhIECrBFREREREREgkABtoiIiIiIiEgQKMAWERERERERCQIF2CIiIiIiIiJBoABbREREREREJAgiOroB/nC73Rw6dIj4+HhMJlNHN0dERMKcx+OhrKyMXr16YTbrXHUw6LteREQ6m9Z833eJAPvQoUNkZ2d3dDNEREQayMvLIysrq6Ob0S3ou15ERDqrQL7vu0SAHR8fDxhvLCEhoU3HcjqdLF68mOnTp2O1WoPRvG5N/RUY9Vdg1F+BUX/5L9R9VVpaSnZ2tu/7SdpO3/UdR/0VGPVXYNRfgVF/BaYzft93iQDbO1QsISEhKF+6MTExJCQk6B+tH9RfgVF/BUb9FRj1l//aq680lDl49F3fcdRfgVF/BUb9FRj1V2A64/e9Jo6JiIiIiIiIBIECbBEREREREZEgUIAtIiIiIiIiEgRdYg62iEhX4vF4qKmpweVyBfW4TqeTiIgIqqurg37s7iYYfWWxWIiIiNA8607E38+WPiuBaY/+slqtWCyWkBxbRKQzUYAtIhJEDoeDw4cPU1lZGfRjezweMjIyyMvLU9DXgmD1VUxMDJmZmURGRgaxddIagXy29FkJTHv0l8lkIisri7i4uJAcX0Sks1CALSISJG63m71792KxWOjVqxeRkZFB/bHqdrspLy8nLi4Os1kzfJrT1r7yeDw4HA4KCwvZu3cvgwcPVp93oEA/W/qsBCbU/eXxeCgsLOTAgQMMHjxYmWwR6dYUYIuIBInD4cDtdpOdnU1MTEzQj+92u3E4HERFRSloaEEw+io6Ohqr1cr+/ft9x5KOEehnS5+VwLRHf/Xs2ZN9+/bhdDoVYItIt6ZvHRGRINMP+u5Df8vORX+PrktD9UUkXOibSkRERERERCQIFGCLiIiIiIiIBIECbBERCap+/frxzDPPBOVYy5Ytw2QyUVxcHJTjiX+WL1/OpZdeSq9evTCZTLz33nst7rNs2TJOPfVUbDYbgwYNYuHChSFvZ7gJ5mdLRERCQwG2iIhw7rnncueddwblWN988w0//elPg3Is6RgVFRWMGTOGF154wa/t9+7dy8UXX8x5553HunXruPPOO7npppv45JNPQtzSzk+fLRGR8KJVxEVEpEUejweXy0VERMtfGz179myHFkkoXXTRRVx00UV+b79gwQL69+/Pk08+CcDw4cP58ssvefrpp5kxY0aomtkt6LMlItK9hFeAXXqIiDeu4pySUpg5s6NbIyJhwOPxUOV0BeVYbrebKoeLCEeNX6spR1stfq3ce8MNN/D555/z+eef8+yzzwLw17/+lRtvvJFFixbxm9/8ho0bN7J48WKys7OZM2cOX3/9NRUVFQwfPpz58+czdepU3/H69evHnXfe6cvamUwmXn75ZT788EM++eQTevfuzZNPPsn3vve9VvXDv/71Lx544AF27dpFZmYmP//5z7n77rt9z7/44os8/fTT5OXlkZiYyFlnncU777wDwDvvvMO8efPYtWsXMTExjBs3jvfff5/Y2NhWtUUMK1eubPBvAGDGjBnNZm7tdjt2u913v7S0FACn04nT6WywrdPpxOPx4Ha7cbvdLX6uPB4PVQ4XFrsz6KtX+/u5ArjxxhtP+mz95S9/4X//93/54IMPeOCBB9i4cSMff/wx2dnZ3H333axatcr32Xr00Ucb9OuAAQO44447uOOOOwCwWCz86U9/YtGiRSxevJjevXvzxBNP+PXZcrlc/OxnP+Ozzz4jPz+frKwsbrvtNt+xvV599VWefvppdu3aRUpKCt///vd57rnnACguLua+++7j/fffp6SkhEGDBvHYY49xySWXnPR63r9bdyjT5f33eeK/UznZ4ZJq5n+0jeJCM6eXVtIzIfglLLuTvUcrmP/RNrYdsPDC7q+0+r4fPB4PsS4z06aF5vPYms95eAXYJgum/A0kYqLG4+no1ohIGKhyuhjxQMcMk93y8AxiIlv+b/7ZZ59lx44djBw5kocffhiAzZs3A3Dffffxhz/8gQEDBpCcnExeXh4zZ87k0UcfxWaz8dprr3HppZeyfft2+vTp0+RrzJs3j8cff5wnnniC5557jmuvvZb9+/eTkpIS0Htas2YNP/zhD3nooYe46qqrWLFiBbfddhs9evTghhtu4Ntvv+X//u//+Nvf/saoUaNwOp189dVXABw+fJirr76axx9/nMsvv5yysjK++OILPPo+aLP8/HzS09MbPJaenk5paSlVVVVER0eftM/8+fOZN2/eSY8vXrz4pFrXERERZGRkUF5ejsPhoMrhYtJTXwf3Tfhp5ZwziI70L0B8+OGH2bp1KyNGjGDu3LkAbNu2DYBf/vKXPPLII/Tr14+kpCQOHDjAeeedx3333YfNZuPNN9/ksssuY/Xq1WRnZwNGkFpdXe07GQHGZ2vevHk88MAD/PnPf+bHP/4xGzZsIDk5udm2OZ1OevbsyauvvkpKSgqrVq3irrvuIikpicsvvxwwTgb85je/4cEHH2Tq1KmUlpayatUqSktLcbvdXHjhhZSVlflGMGzbtg273d6gfV4Oh4OqqiqWL19OTU2NX/3X2eXk5HR0Ezq1tUdNvL3HTJXLBJi56NkvuHaQm6GJ+j/3RB4PrDhi4r19ZhxuE2DicGVFRzery8iKNYXs81hZWRnwPuEVYNviADDhgZoqiIzs4AaJiHS8xMREIiMjiYmJISMjA6gLAh5++GGmTZvm2zYlJYUxY8b47j/yyCO8++67/Oc//2H27NlNvsYNN9zA1VdfDcBjjz3GH//4R1avXs2FF14YUFufeuopLrjgAu6//34AhgwZwpYtW3jiiSe44YYbyM3NJTY2lksuuQSPx0NCQgLjx48HjAC7pqaG73//+/Tt2xeAUaNGBfT6Ejxz585lzpw5vvulpaVkZ2czffp0EhISGmxbXV1NXl4ecXFxREVFEeHouAAtPiHerxNXAAkJCcTExJCYmMjgwYMBOHjwIGB8di677DLftn379mXKlCm+++PGjeOjjz5i2bJl3H777YBRBzwqKqpB/9x444385Cc/AeCJJ57gT3/6E1u3bvXrszV//nzAyAD17duX9evX88EHH3D99dcDxudtzpw53Hvvvb59zj33XMA4EbJmzRo2b97MkCFDABg9enSTr1VdXU10dDRnn302UVFRLbatM3M6neTk5DBt2jSsVmtHN6fTKat2Mu+Dbby/8zAAI3vFc6SolCPVJl7cYuHGyX25e+ogbNauPZIhWI6W2/nVe5v5bM9RACb2S2Jc9DFOG3+qX1NHwl1NTQ2b160J2eexsROGLQmvv1pENB5MRoBtL4eYxI5ukYh0c9FWC1seDs4cVLfbTVlpGfEJ8X4PEW+r0047rcH98vJyHnroIT788ENfwFpVVUVubm6zx6n/wzs2NpaEhASOHDkScHu2bt3aICgBmDJlCs888wwul4tp06bRt29fBg0axPnnn88ll1zCD37wA2JiYhgzZgwXXHABo0aNYsaMGUyfPp0rrriixUyftCwjI4OCgoIGjxUUFJCQkNBo9hrAZrNhs9lOetxqtZ70I8nlcmEymTCbzZjNZmJt1mY/V4F+VgIRyBBxL2/bAd/1hAkTGrStqc9WXl5eg+3qHwtgzJgxvvvx8fEkJCRw9OhRv973Cy+8wKuvvkpubi5VVVU4HA7Gjh2L2WzmyJEjHDp0iKlTpzZ6rA0bNpCVlcWwYcP86gOz2YzJZGr079tVdaf3Eixf7znG3W+v52BxFWYTzD5/MLec1ZcPFn3Md55+/OObA/x1xX5W7C7imR+NZXhmQssH7cY+3VLAL/+1gWMVDiItZu69cCg/npDFxx9/xDlD0/Xvyw9Op5OK3aH7PLbmmOEVYJvNEBkDjgpwatiFiISeyWTyO9vVErfbTU2khZjIiKAHDU05cW7yPffcQ05ODn/4wx8YNGgQ0dHRXHHFFTgcjmaPc+IXlMlkwu12B7298fHxrF27lqVLl/LBBx/w0EMP8fDDD/PNN9+QlJRETk4OK1asYPHixTz33HP8+te/ZtWqVfTv3z/obQknkyZNYtGiRQ0ey8nJYdKkSSF5vZY+Vx3xWQlUR3+23nzzTe655x6efPJJJk6ciMlkYsGCBaxevRqgyRMjXi09L+HFUePmqZwd/Gn5bjwe6JMSw9NXjWV832ScTic2Czw8cwRTR2Twy39tYHtBGZc9/xX3XjiUn0zpj9kcXnONKx01/PbDrbyxyjg5PSwjnmd+NJZhGQma298NdM5vnVCKNIaJYy/v2HaIiHQikZGRuFwtL8b21VdfccMNN3D55ZczatQoMjIy2LdvX+gbWGv48OG+OdX12zRkyBDfwkkRERFMnTqVhx9+mHXr1rFv3z6WLl0KGMHHlClTmDdvHt999x2RkZG8++677db+rqK8vJx169axbt06wCjDtW7dOt9Ihblz53Ldddf5tr/lllvYs2cP9957L9u2bePFF1/k7bff5q677uqI5ncqnfWz9dVXXzF58mRuu+02xo0bx4ABA9izZ4/v+fj4ePr168eSJUsa3X/06NEcOHCAHTt2hKyN0jXsLChj1gtfseBzI7i+6rRsFt1xFuP7njw66ILh6Xx859lMHZ6Gw+Xmtx9u5X/+sopDxVUd0PKOsS6vmIv/+KUvuL75rP68d/sUhmWEdza/OwmvDDZApHHG2KQMtoiIT79+/Vi1ahX79u0jLi6uyQzY4MGD+fe//82ll16KyWTi/vvvD0kmuil33303p59+Oo888ghXXXUVK1eu5Pnnn+fFF18E4IMPPmDPnj2ceeaZRERE8MUXX+B2uxk6dCirVq1iyZIlTJ8+nbS0NFatWkVhYSHDhw9vt/Z3Fd9++y3nnXee7753rvT111/PwoULOXz4cINpAf379+fDDz/krrvu4tlnnyUrK4tXXnlFJbrovJ+twYMH89prr/HJJ5/Qt29f/vKXv/DNN980GM3x0EMPccstt5CWlsZFF11EWVkZX331FT//+c8555xzOPvss/nBD37AU089xaBBg9i2bRsmkyngtRWka3K7Pby2ch/zP9qGvcZNcoyV+d8fzYUjM5rdLzXOxsvXncY/VufxyAdbWLH7GBc+s5zfXj6K743p1U6tb381LjcvfLabPy7dicvtITMxiievHMPkQakd3TQJsjAMsJXBFhE50T333MP111/PiBEjqKqq4q9//Wuj2z311FP85Cc/YfLkyaSmpvLLX/6yVQuAtNapp57K22+/zQMPPMAjjzxCZmYmDz/8MDfccAMASUlJ/Pvf/+ahhx6iurqawYMH849//INTTjmFrVu3snz5cp555hlKS0vp27cvTz75ZED1nsPFueee2+zq6gsXLmx0n++++y6EreqaOutn62c/+xnfffcdV111FSaTie9///vceuutfPzxx75trr/+eqqrq3n66ae55557SE1N5YorrvA9/69//Yt77rmHq6++moqKCgYNGsTvfve7kLVZOo+C0mru+ed6vthpLMx1zpCePHHFaNIS/FvAzmQycc3EPpwxIIW73l7P+rxi/u8f37F0awHzLhtJYnT3mnu8/1gFd761ju9yiwG4ZHQmj84aRWJM93qfYjB5ukB9ktLSUhITEykpKTlp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Y2ppafG7KEpSenq6amtrg7Sqrtfa2qqSkhINHz5cAwYMkCTV1tbK7XYrKSnJr/bMLGpra8+aVdu+89XU19fr1KlTYZX5smXL9Le//U1z585tt4+8/B04cEDPPvus+vbtq7feekv33nuv7r//fr344ouSPj/f851HbW2t0tLS/PbHxMQoOTk5IJmGUl4PP/ywvve976lfv36KjY3VwIEDVVJSouLiYknkdT6hlE1H1oLQEC7X0kCi13cMvb7j6PXO0OsvXihlE8heH+OoGmFlypQp2r17tzZu3BjspYSsmpoaTZs2TWVlZYqLiwv2ckJea2ur8vLy9Pjjj0uSBg4cqN27d2vhwoWaOHFikFcXel599VUtWbJEL7/8svr376/t27erpKREvXr1Ii8AAUGvvzB6vTP0emfo9fiiiHgFOzU1VdHR0e3uBnnkyBFlZGQEaVVda+rUqXrjjTe0bt06XXrppb7tGRkZampqUl1dnV/9mVlkZGScNau2feer8Xq9io+PD5vMt27dqqNHj2rQoEGKiYlRTEyM3nnnHf36179WTEyM0tPTyesMl1xyia655hq/bVdffbWqq6slfX6+5zuPjIwMHT161G//6dOndfz48YBkGkp5lZaW+n6ynZubqzvuuEM//vGPfa+gkNe5hVI2HVkLQkO4XEsDhV7fMfR6Z+j1ztDrL14oZRPIXh8RA7bb7dbgwYNVXl7u29ba2qry8nLl5+cHcWWBZ2aaOnWqVq5cqbVr16pPnz5++wcPHqzY2Fi/LPbt26fq6mpfFvn5+dq1a5ffN3NZWZm8Xq/vgpufn+93jLaatmOES+YjRozQrl27tH37dt+fvLw8FRcX+/5OXp8bPnx4u0fBfPDBB8rJyZEk9enTRxkZGX7nUV9fr8rKSr+86urqtHXrVl/N2rVr1draqqFDh/pq1q9fr+bmZl9NWVmZrrrqKvXo0cNXc75MQ8HJkycVFeV/mY2OjlZra6sk8jqfUMqmI2tBaAiXa2ln0eudodc7Q693hl5/8UIpm4D2eke3RAtjy5YtM4/HYy+88ILt2bPHfvjDH1pSUpLf3SC/Cu69917r3r27VVRU2OHDh31/Tp486auZPHmyZWdn29q1a62qqsry8/MtPz/ft7/tURQjR4607du325o1a6xnz55nfRRFaWmp7d2715555pmzPooiHDM/886i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\n"
          },
          "metadata": {}
        }
      ],
      "execution_count": 10
    },
    {
      "cell_type": "markdown",
      "source": [
        "## selu激活函数，可以加速收敛，效果相对于relu更好"
      ],
      "metadata": {
        "collapsed": false,
        "id": "byim8TZNWpTx"
      }
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-17T02:42:02.602428Z",
          "start_time": "2025-01-17T02:42:01.525907Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "l8ox8ApeWpTx",
        "outputId": "72955119-6e24-4342-8b1b-3c50bee5310b"
      },
      "source": [
        "# dataload for evaluating\n",
        "\n",
        "# load checkpoints\n",
        "model.load_state_dict(torch.load(\"checkpoints/selu/best.ckpt\", map_location=\"cpu\"))\n",
        "\n",
        "model.eval()\n",
        "loss, acc = evaluating(model, val_loader, loss_fct)\n",
        "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "<ipython-input-11-4f1c4adabdd2>:4: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n",
            "  model.load_state_dict(torch.load(\"checkpoints/selu/best.ckpt\", map_location=\"cpu\"))\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "loss:     0.3673\n",
            "accuracy: 0.8876\n"
          ]
        }
      ],
      "execution_count": 11
    },
    {
      "cell_type": "code",
      "source": [],
      "metadata": {
        "id": "9DUxh2BjcduL"
      },
      "execution_count": null,
      "outputs": []
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.10.8"
    },
    "orig_nbformat": 4,
    "colab": {
      "provenance": [],
      "gpuType": "T4"
    },
    "accelerator": "GPU",
    "widgets": {
      "application/vnd.jupyter.widget-state+json": {
        "12017d6b533f4fe2a4085f50905ae448": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "HBoxModel",
          "model_module_version": "1.5.0",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HBoxModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HBoxView",
            "box_style": "",
            "children": [
              "IPY_MODEL_9ea0c658e6204fb395ca23e8e6bd6039",
              "IPY_MODEL_e9e545daa3774bdb8431d88076cd5fe8",
              "IPY_MODEL_a06b865199ee441ca032bbeed9cea15d"
            ],
            "layout": "IPY_MODEL_c7096770fb5c48be88bc787bf5adaee8"
          }
        },
        "9ea0c658e6204fb395ca23e8e6bd6039": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "HTMLModel",
          "model_module_version": "1.5.0",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HTMLModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HTMLView",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_2105ca87b9d54be8a5a307bdba6c4bcf",
            "placeholder": "​",
            "style": "IPY_MODEL_fef0af365c604b1eb7b02ac9cf5dae82",
            "value": " 27%"
          }
        },
        "e9e545daa3774bdb8431d88076cd5fe8": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "FloatProgressModel",
          "model_module_version": "1.5.0",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "FloatProgressModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "ProgressView",
            "bar_style": "danger",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_b84f4f37873e475094cc86f7225999a9",
            "max": 375000,
            "min": 0,
            "orientation": "horizontal",
            "style": "IPY_MODEL_03c18b8d85d245bc893378847eb978c5",
            "value": 101250
          }
        },
        "a06b865199ee441ca032bbeed9cea15d": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "HTMLModel",
          "model_module_version": "1.5.0",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HTMLModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HTMLView",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_f54afdcc184f40e7a1cfe29da99ebe60",
            "placeholder": "​",
            "style": "IPY_MODEL_289d71f847e148dda2fa9149bfdd652b",
            "value": " 101250/375000 [17:11&lt;44:59, 101.40it/s, epoch=26]"
          }
        },
        "c7096770fb5c48be88bc787bf5adaee8": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "model_module_version": "1.2.0",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "2105ca87b9d54be8a5a307bdba6c4bcf": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "model_module_version": "1.2.0",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "fef0af365c604b1eb7b02ac9cf5dae82": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "DescriptionStyleModel",
          "model_module_version": "1.5.0",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "DescriptionStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "description_width": ""
          }
        },
        "b84f4f37873e475094cc86f7225999a9": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "model_module_version": "1.2.0",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "03c18b8d85d245bc893378847eb978c5": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "ProgressStyleModel",
          "model_module_version": "1.5.0",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "ProgressStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "bar_color": null,
            "description_width": ""
          }
        },
        "f54afdcc184f40e7a1cfe29da99ebe60": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "model_module_version": "1.2.0",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "289d71f847e148dda2fa9149bfdd652b": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "DescriptionStyleModel",
          "model_module_version": "1.5.0",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "DescriptionStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "description_width": ""
          }
        }
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}